<template>
  <RenderTable></RenderTable>
</template>
<script lang="jsx" setup name="v3Table">
/* 页面列表展示组件，table格式。对外暴露更新列表、选中数据、接口数据的方法。内部包含处理单项数据的编辑、删除、详情等功能。 */
  import { useSlots, reactive, ref, onMounted } from 'vue'
  import { trim, isNotEmpty, isFunction, isNumber, isArray, isString, parseSpecField, parseTemplate, deepClone } from '@/utils/tools.js'
  import { factory } from '@/utils/factory.js'
  import { useFetch } from '@/hooks/fetch.js'
  import { usePageInfo } from '@/hooks/pageInfo.js'
  import v3Button from './v3Button.vue'
  import datePicker from './datePicker.vue'
  import { watchEffect } from 'vue'
  const emit = defineEmits(['up', 'fetch']);
  const slots = useSlots();
  const { pageInfo } = usePageInfo()
  const props = defineProps({
      schema: {
        type: Object,
        default: () => {}
      },
      columnSlotIdx: {
        type: [Number, Array],
        default: 0
      },
      data: {
        type: Array,
        default: () => []
      },
      isAlone: {
        type: Boolean,
        default: false
      },
      isDailog: { // 是否为弹窗内的表格
        type: Boolean,
        default: false
      },
      columns: {
        type: Array,
        default: () => []
      }
  })

  const { columnSlotIdx, data:list, isAlone, columns, isDailog } = props
  const schema = {
      "isLoad": true,
      "isBorder": true,
      "stripe": true,
      "types": ['index'], // ['selection', 'index']
      "align": 'center',
      "fixedDirection": "right",
      "showOverflowTooltip": true,
      "pageFields": {},
      "operationWidth": null,
      "operationFixed": 'right',
      ...props.schema,
  }
  // console.log('schema------', schema)
  if(isAlone) schema.columns = columns
  const table = reactive({
    data: isAlone ? list : [],
    dataCopy: [],
    loading: false,
    rowClass: [],
    notPagination: isAlone, // 由 tables数据控制是否显示分页，默认false
    isFrontPagination: false, // 由前端控制分页，默认false
  })

  const page = reactive({
    current: 1,
    size: schema.pageFields?.size || 20,
    total: 0,
    sizeOptions: schema.pageFields?.sizeOptions || [10, 20, 30, 40, 50],
    sizeField: schema.pageFields?.sizeField || 'pageSize',
    numField: schema.pageFields?.numField || 'pageNum',
    dataField: schema.pageFields?.dataField || 'resBody',
    totalField: schema.pageFields?.totalField || 'totalCount',
    dialogSize: schema.pageFields?.dialogSize || 15,
  })
  // console.log('page------', page)
  const emptyText = ref('暂无数据!')
  const splitEnums = {
      'value': val => {
        return val.replace(/'|"/g, '')
      },
      /* 以参数的形式接收，返回class值：去除逗号、单引号、双引号加空格 */
      'rowClass': val => {
        const className = val.split(',')
        return className.join(' ').replace(/'|"/g, '')
      },
      'cellClass': val => {
        const className = val.split(',')
        return className.join(' ').replace(/'|"/g, '')
      },
      "style": val => {
        return val.replace(/'|"/g, '')
      }
  }
  const privateFn = {
    getSelection() {
      return {"tableSelected": [multipleSelection.value] || []}
    },
    getLength() {
      return { tableSize: page.total }
    },
    update: async (config, params = {}) => {
      const request = config.request || config
      const { current, totalField, dataField } = page
      let { size } = page
      // console.log('v3table---update---params---', current, size, params)
      if (params?.data?.length) {
        if (params?.size) size = params.size
        /* 若传入 table 数据时，根据page.total判断是否展示分页 */
        if (isArray(params?.data) && params?.data?.length > size ) {
          table.notPagination = false
          table.isFrontPagination = true
          !table.dataCopy.length && (table.dataCopy = deepClone(params.data))
          table.data = table.dataCopy.slice(0, (1 * size ))
        } else {
          table.data = [...params.data]
        }
        page.total = params.total || params.data.length || 0
      } else if (params instanceof Object && request.url) {
        try {
          table.loading = schema.isLoad
          /* 配置项的默认参数 */
          // console.log('v3table---update---config--111-', request)
          Object.assign(request, {"params": {[page.numField]: current, [page.sizeField]: size, ...params}})
          /* 根据配置自动获取 query 组件的查询数据 */
          !config.notQueryData && emit('up', {index: 0, actOn: 'query', rangeFn:'getData'}, {}, obj => {
            // console.log('v3table---update---config--122221-', obj)
            Object.assign(request.params, obj)
          })
          // console.log('v3table---update---config--222-', config, request)
          const { result = {}, error } = await useFetch(request)
          // console.log('v3table---update---error---',result, error)
          if (error) {
            isNotEmpty(error) && emit('fetch', {"widgetName": 'table', ...error})
            return error
          }
          if (!result) return
          table.data = result?.list || result[dataField] || result
          page.total = result?.totalCount || result[totalField] || table.data.length || 0
          /* 若接口返回数据时，默认展示分页 */
          table.notPagination = false
          table.isFrontPagination = false
          return result
        } catch (error) {
          table.loading = false
          return error
        } finally {
          table.loading = false
        }
      }
    },
    fetch: async (config = {}, params) => {
      // console.log('v3table---fetch---params---', config, params)
      config.wgtName = 'table'
      emit('up', {index: schema.index, ...config, 'actOn': 'operation', 'rangeFn': 'fetch'}, params, res => {
          // fn && fn(config, data).then(res => {
          console.log('v3Dialog--commit--', res)
          const { successCb, errorCb } = config?.request
          if (res.isError) {
            (errorCb && privateFn[errorCb]) ? privateFn[errorCb](config, params) : isNotEmpty(res) && emit('fetch', {"widgetName": 'table',...res})
              return
          }
          // })
      })
    },
    pageEvent: (page) => {
      // console.log('v3table---pageEvent---', page)
      emit('event', {"widgetName": 'table', ...page})
    }
  }
  /* 处理table操作栏的按钮点击事件 */
  const handler = (config, data, idx) => {
    console.log('v3-table---handler', config, data)
    const { actOn, rangeFn, isCustom, hookFn, link } = config
    if (link) {
      config.realLink = parseTemplate(link, data)
    }
    if (!actOn && privateFn[rangeFn]) {
      return privateFn[rangeFn](config, data)
    } else if (isCustom || hookFn) {
      return emit('event', {"widgetName": 'table', ...config, data})
    }
    data.idx = idx
    return emit('up', {index: schema.index, ...config}, data)
  }
  const transferData = []
  const widgetData = {} // 组件数据
  const handleSizeChange = (val) => {
    page.current = 1
    page.size = val
    privateFn.pageEvent({widgetName: 'table', trigger: 'sizeChange', ...page})
    /* if (table.isFrontPagination) {
      handlePagination(val)
    } else { */
      emit('up', {index: schema.index, actOn: 'query', rangeFn: 'search' }, (handle, config) => {
        // console.log('v3table-handleSizeChange--', handle, val)
        handle instanceof Function &&  handle({ index: schema.index, actOn: 'table', rangeFn: 'update', actBy: 'table' }, {current: 1, size: val})
      })
    // }
  }
  const handleCurrentChange = (val) => {
    page.current = val
    privateFn.pageEvent({ widgetName: 'table', trigger: 'currentChange', ...page })
    if (table.isFrontPagination) {
      handlePagination()
    } else {
      emit('up', {index: schema.index, actOn: 'query', rangeFn: 'search'}, (handle, config) => {
        // console.log('v3table-handleSizeChange--', handle, val)
        handle instanceof Function && handle({ index: schema.index, actOn: 'table', rangeFn: 'update', actBy: 'table' }, {current: val})
      })
    }
  }
  const handlePagination = (val) => {
    /* 前端分页 */
    let x = 0
    let { current, dialogSize } = page
    const size = val || isDailog ? dialogSize : page.size
    if (page.total) {
      x = (current - 1) * size/* 当前页第一条数据的下标 */
      /* 修改page.size后，可能出现页数 * 每页展示数量 大于 数据总量的情况，修改页码为当前每页展示数量的最后一页 */
      if (x > page.total) {
        current = Math.ceil(page.total / size) // 向上取整
        x = (current - 1) * size // 重新计算第一条数据的下标
      }
    }
    const first = current > 1 ? x : 0
    table.data = table.dataCopy.slice(first, (current * size ))
  }
  const multipleSelection = ref([])
  const handleSelectionChange = val => {
    multipleSelection.value = val
  }
  const init = () => {
    /* 匹配函数名称 */
    const regexFn = /\b(\w+)\(/;
    /* 匹配函数的参数 */
    const regexParam = /\(([^)]+)\)/;
    // console.log('v3table---init---', schema)
    schema.columns.map(item => {
      const { prop, config = {} } = item
      const { tableLogic, enums } = config || {}
      /* 配置‘ | ’方法：数据格式化转换 */
      if (prop && prop.indexOf('|') > -1) {
        const [pr, fn] = prop.split('|')
        item["prop"] = trim(pr)
        item["auxFunc"] = trim(fn)
        if (!item.auxFunc || !factory[item.auxFunc]) {
          console.error('工厂函数缺失! 请自行提供auxFunc函数! ')
        }
      }
      /* 配置枚举数据 */
      if (item.enums || enums) {
        Object.assign(transferData, {[prop]: item.enums || enums})
        const enumKeys = Object.keys(enums || {})
        enumKeys.length && (widgetData[prop] = enumKeys.map(key => {
        const value = enums[key]
          return {
            "value": value,
            "key": key
          }
        }))
      }
      if (tableLogic) {
            /* 先判断是否包含多个逻辑： "obj?.zoneName == '小组零' # [value('优'), class('p-1')]; obj?.zoneName == '小组六' # value('良')" 
            *  或者 "obj?.zoneName == '小组零' # value('优'), obj?.zoneName == '小组六' # value('良')"
            */
            if (/\#[\s\S]*\];\s*/.test(tableLogic) || (/\#\s\S*\);\s*/.test(tableLogic) && !/[\[\]]/.test(tableLogic))) {
              const logicArr = /\#[\s\S]*\];\s*/.test(tableLogic) ? tableLogic.replace('];', ']/./').split('/./') : tableLogic.split(';')
              item["logic"] = []
              item["fn"] = []
              logicArr.forEach(obj => {
                const [logic, fn] = obj.split('#')
                item["logic"].push(trim(logic))
                const fnArr = /[\s\S]*\]\s*/.test(fn) ? trim(fn).replace(/[\[\]]/g, "").split(", ") : trim(fn);
                /* 可以同时设置 calss/style/value */
                if (Array.isArray(fnArr)) {
                  fnArr.forEach((el, i) => {
                    const [val0, name] = el.match(regexFn);
                    const [val1, params] = el.match(regexParam);
                    const val = splitEnums[name](params)
                    item[name]?.length ? item[name].push(val) : item[name] = [val]
                  })
                } else {
                  const [val0, name] = fnArr.match(regexFn);
                  const [val1, params] = fnArr.match(regexParam);
                  const val = splitEnums[name](params)
                  item[name]?.length ? item[name].push(val) : item[name] = [val]
                }
              })
            } else {
              const [logic, method] = tableLogic.split('#')
              item["logic"] = trim(logic)
              const fn = method.replace(/[\[\]]/g, "").split(", ");
              /* 可以同时设置 calss/style/value: "obj?.roleName == '管理员' && obj?.postName == '后端开发' # [class('p-4'), value('优秀'), style('color: red;font-size: 16px;')] */
              fn.length && fn.forEach((el, i) => {
                  const [val0, name] = el.match(regexFn);
                  const [val1, params] = el.match(regexParam);
                  item[name] = splitEnums[name](params)
              })
            }
      }
      table.rowClass.push(item['rowClass'])
    })
    /* 如果有插槽 */
    if (columnSlotIdx || columnSlotIdx?.length) {
      if (isArray(columnSlotIdx)) {
        columnSlotIdx.forEach((item, i) => {
          schema.columns.splice(item, 0, {type: 'slot', idx: item, isShow: true})
        })
      } else {
        schema.columns.splice(columnSlotIdx, 0, {type: 'slot', idx: columnSlotIdx, isShow: true})
      }
    }
    // console.log('columnList---', schema.columns)
  }
  onMounted(() => {
    // console.log('v3table---onMounted---', factory.validateByEval("obj.type==='page'", {type: 'directory'}))
    init()
  })
  // init()
  watchEffect(() => {
    /* 弹框中包含 table 组件 */
    /* 只监听 data 数量大于 size 的情况，table.data 数据来源于接口时，必须翻页 */
    if (table.data.length > page.dialogSize && !page.total) {
      /* 复制一份 table.data 数据 */
      !table.dataCopy.length && (table.dataCopy = deepClone(table.data))
      table.isFrontPagination = true
      page.total = table.data?.length
      page.size = page.dialogSize
      table.notPagination = false
      if (page.current == 1) table.data = table.dataCopy.slice(0, page.dialogSize)
    }
  })
  const handleFrontPagination = (arr) => {
    // console.log('v3table---handleFrontPagination---', val)
    
  }
  const handleRowClass = ({row, rowIndex}) => {
    let val
    schema.columns.map((item, i) => {
      const { rowClass = undefined, logic = undefined } = item
      if (rowClass) {
        if (isArray(logic)) {
          logic.map((obj, idx) => {
            if (factory.validateByEval(obj, row)) val = rowClass[idx] || rowClass
          })
        } else if (factory.validateByEval(logic, row)) {
          val = isArray(rowClass) ? rowClass[i] : rowClass
        }
      }
    })
    return val
  }
  const handleCellClass = ({column, columnIndex, row, rowIndex}) => {
    console.log('handleCellClass', column, columnIndex, row, rowIndex)
  }
  const customFn = (config, data, idx, fn) => {
    console.log('v3table---customFn---', config, data)
    fn(config, data)
    handler(config, data, idx)
  }
  defineExpose({ "update": privateFn.update, "getSelection": privateFn.getSelection, getLength: privateFn.getLength, fetch: privateFn.fetch })
  const RenderTable = () =>
  <>
    <el-table class={'table'} key={new Date().getTime()} data={table.data} row-class-name={handleRowClass} v-loading={table.loading} stripe={schema.stripe} show-overflow-tooltip={schema.showOverflowTooltip} onSelectionChange={handleSelectionChange} empty-text={emptyText.value} border={schema.isBorder} height={schema.height} style={schema.style} tabble-layout='fixed'>
      {
        schema.types.map(item => {
          return <el-table-column align={schema.align} type={item} label={item === 'index' ? '序号': ''} width='55'></el-table-column>
        })
      }
      {
        slots.tableDefault && <slots.tableDefault row={table.data}></slots.tableDefault>
      }
      {
        schema.columns.map((item, i) => {
          const { label, prop, method, width, isShow, logic, slot, cellClass, style, button, tag, config = {} } = item
          const { filterable, clearable, multiple, enums } = config
          const btnConfig = {
            label, style, ...button
          }
          if (!isShow && !table.notPagination) return
          return item.type === 'expand'
          ? <el-table-column type='expand' width={width} label={label} align={schema.align} class-name={cellClass}>
            {{
              default: scope => {
              const obj = scope.row
              if (!obj[prop]) return
              const list = Object.keys(obj)
              return <el-descriptions
                  title={label}
                  column={1}
                  size="small"
                  style="padding: 0 15px">
                {
                  list.map(el => 
                  <el-descriptions-item label={`${el} : `}>{obj[el]}</el-descriptions-item>
                  )
                }
                </el-descriptions>
              }
            }}
            </el-table-column>
            : item.type === 'slot' && item.idx === i
            ? <el-table-column label={label} width={width} align={schema.align} class-name={'slotColumnParent'} show-overflow-tooltip>
            {{
              default: scope => {
                const obj = scope.row
                if (isArray(columnSlotIdx)) {
                  const idx = columnSlotIdx.findIndex(n => n == [item.idx])
                  // console.log(idx, slots)
                  return slots?.tableColumn ? table.notPagination ? slots.tableColumn(obj)[idx] : slots.tableColumn(obj)[0].children[idx] : null
                } else {
                  return slots?.tableColumn && slots.tableColumn(obj)
                }
              }
            }}
            </el-table-column>
            : item.type !== 'operation'
            ? <el-table-column label={label} width={width} align={schema.align} show-overflow-tooltip class-name={cellClass}>
              {{
                default: scope => {
                  // 操作根据页面信息的数据进行
                  const obj =  {...pageInfo?._assert_, ...pageInfo?._storage_, ...scope.row};
                  let row = scope.row
                  /* 添加支持：name属性可为x.xx.xxx.xxxx(多层嵌套)的取值方式 */
                  if (prop && prop.indexOf('.') !== -1) obj[prop] = parseSpecField(prop, obj)
                  let i // 数组逻辑为真的第一个下标
                  // 验证逻辑
                  if (isArray(logic)) {
                    logic.map((item, idx) => {
                      if (factory.validateByEval(item, obj)) i = idx
                    })
                  } else {
                    if (factory.validateByEval(logic, obj)) i = 0
                  }
                  let result
                  if (item.enums || enums) {
                    result = transferData[prop][obj[prop]]
                  } else if (item.auxFunc) {
                    const fn = item.auxFunc;
                    result = (!factory[fn] || !fn) ? obj[prop] : factory[fn](obj[prop])
                  } else if (logic) {
                    result = isArray(item.value) ? item.value[i] || obj[prop] : isNumber(i) ? item.value || obj[prop] : obj[prop]
                  } else {
                    result = obj[prop]
                  }
                  const handleClick = () => {
                    if (!scope.row.isEditing && method && isFunction(method)) {
                      method(item, obj, emit, index);
                    }
                  };
                  return (
                    <div class={['pd-7', isNumber(i) && isArray(cellClass) ? cellClass[i] : isString(cellClass) && isNumber(i) ? cellClass : '']}>
                      {
                      !row.isEditing
                      ? <span style={isNumber(i) && isArray(style) ? style[i] : style} 
                              onClick={handleClick}>{result}</span>
                      : (tag === 'input' || (!tag && item.showIn?.indexOf('edit') > 0))
                      ? <el-input modelValue={result} onInput={(value) => row[prop] = value} />
                      : tag === 'select'
                      ? <el-select
                          modelValue={result}
                          filterable={filterable || true}
                          clearable={clearable || true}
                          multiple={multiple}
                          onChange={(val) => row[prop] = val}>
                            {
                              widgetData[prop]?.length && widgetData[prop].map(el => {
                                return <el-option value={el['key']} label={el['value']} key={el['key']}></el-option>
                              })
                            }
                          </el-select>
                      : (tag === "date" || tag === "datetime" || tag === 'month' || tag === 'year')
                      ? <datePicker
                          type={tag}
                          v-model={result}
                          value-format={config?.valueFormat}
                          onChange={(val) => {if (val) row[prop] = val}}
                          config={config}>
                        </datePicker>
                      : <span style={isNumber(i) && isArray(style) ? style[i] : style} 
                              onClick={handleClick}>{result}</span>
                    }
                    </div>
                  )
                  {/* return (
                      <div class={['pd-7', isNumber(i) && isArray(cellClass) ? cellClass[i] : isString(cellClass) && isNumber(i) ? cellClass : '']}> {
                          item.isDetail // 详情弹框
                          ? <v3Button onClick={() => method && isFunction(method) ? method(obj, scope.$index) : handler(item, obj, emit, schema.index) } config={{label: item.label || obj[prop]}}/>
                            // 枚举值转化
                          : item.enums || item.config?.enums
                          ? <span style={isNumber(i) && style}>{transferData[prop][obj[prop]]}</span>
                          // 点击时使用传入的函数处理
                          : method && isFunction(method)
                          ? <span style={isNumber(i) && style} onClick={() => method(item, obj, emit, schema.index)}>{ item.label }</span>
                          // 使用辅助函数处理展示内容
                          : item.auxFunc
                          ? <span style={isNumber(i) && style}>
                            {
                              () => {
                                const fn = item.auxFunc;
                                if (!factory[fn] || !fn) {
                                  return obj[prop]
                                }
                                const text = factory[fn](obj[item.prop])
                                return text
                              }
                            }
                            </span>
                          : logic
                          ? <span style={isNumber(i) && isArray(style) ? style[i] : style}>
                            {
                              isArray(item.value) ? item.value[i] || obj[prop] : isNumber(i) ? item.value || obj[prop] : obj[prop]
                            }
                            </span>
                          : <span>
                            {obj[prop]}
                          </span>
                        }</div>
                      ) */}
                    }
              }}
              </el-table-column>
              // 操作按钮列
            : (isArray(item.operations) && item.operations.length > 0) &&
              <el-table-column label={label} width={schema.operationWidth} align={schema.align} fixed={schema.fixedDirection} class-name={'operationColumn'}>
                {{
                    default: scope => {
                      const obj =  {...pageInfo?._assert_,...pageInfo?._storage_,...scope.row};
                      const edit = () => {
                        scope.row['isEditing'] = true;
                      }
                      return (
                        <div>
                          {
                            // 操作: 可根据表格中的数据控制操作按钮是否显示
                            item.operations.map(el => {
                              const isShow = factory.validateByEval(el.showLogic, obj);
                              const { type, className, disabled, method, label, button, isEditRow = false } = el;
                              const btnConfig = { type, className, disabled, label, isText: true, ...button };
                              const confirm = () => {
                                scope.row['isEditing'] = false;
                                scope.row['isEditBtn'] = label === '编辑' || label === '修改';
                                method && isFunction(method) ? customFn(el, scope.row, schema.index, method) : handler(el, scope.row, schema.index)
                              }
                              return isShow && (label === '编辑' && isEditRow)
                              ? <el-popover placement={'left'}>
                                {{
                                  default: () => (
                                    <div>
                                      <el-button size={'small'} onClick={() => scope.row['isEditing'] = false}>取消</el-button>
                                      <el-button type={'danger'} size={'small'} onClick={confirm}>确认</el-button>
                                    </div>
                                  ),
                                  reference: () => (<v3Button config={btnConfig} onClick={edit}/>)
                                }}
                                </el-popover>
                              : isShow && <v3Button config={btnConfig} onClick={confirm}/>
                            })
                          }
                        </div>
                      )
                    }
                  }}
              </el-table-column>
        })
      }
    </el-table>
    {
      !table.notPagination && <el-pagination class={'pagination'}
            currentPage={page.current}
            pageSize={page.size}
            page-sizes={page.sizeOptions}
            layout={!table.isFrontPagination ? 'total, sizes, prev, pager, next, jumper': 'total, prev, pager, next, jumper'}
            total={page.total}
            onSizeChange={handleSizeChange}
            onCurrentChange={handleCurrentChange}
      />
    }
    
  </>

</script>
<style>
.pd-7{
padding: 7px 0;
}
.table.el-table .el-table__body .el-table__cell {
padding: 0;
}
.slotColumnParent{
padding: 0 !important;
}
.pagination{
margin: 20px 5px;
justify-content: right;
}
.operationColumn div{
padding: 0 4px!important;
}
.operationColumn .el-button{
padding: 8px 10px;
}
.operationColumn .el-button+.el-button{
margin-left: 0!important;
}
</style>